package com.tjbank.cssys.service.impl;

import com.tjbank.cssys.api.LoginService;
import com.tjbank.cssys.dto.LoginDTO;
import com.tjbank.cssys.dto.UserDTO;
import com.tjbank.cssys.framework.base.enums.YesOrNoEnum;
import com.tjbank.cssys.framework.base.exception.ServiceException;
import com.tjbank.cssys.framework.base.util.GeneratIDUtil;
import com.tjbank.cssys.mybatis.IamLogDao;
import com.tjbank.cssys.mybatis.IamUserDao;
import com.tjbank.cssys.mybatis.entity.IamLog;
import com.tjbank.cssys.mybatis.sqlbuilder.IamLogSqlBuilder;
import com.tjbank.cssys.mybatis.sqlbuilder.IamUserSqlBuilder;
import com.tjbank.cssys.service.config.shiro.ShiroTokenCacheManager;
import com.tjbank.cssys.service.config.shiro.stateless.token.TokenGenerator;
import com.tjbank.cssys.service.config.shiro.stateless.token.TokenInfo;
import com.tjbank.cssys.service.config.shiro.stateless.token.TokenParameter;
import com.tjbank.cssys.service.convert.UserConvert;
import com.tjbank.cssys.service.enums.LogTypeEnum;
import com.tjbank.cssys.service.util.Const;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.Instant;
import java.util.*;

/**
 * 登录
 * 
 * Package : com.tansun.magicube.iam.service.impl
 *
 * @author -- lijiangtao 2020年5月3日 下午4:09:50
 */
@Service
public class LoginServiceImpl implements LoginService {

	@Autowired
	private IamUserDao userDao;

	@Autowired
	private RedisTemplate<String, Object> redisTemplate;

	@Autowired
	private ShiroTokenCacheManager shiroTokenCacheManager;

	@Autowired
	private IamLogDao logDao;


	/**
	 * 请求登录验证用户
	 *
	 * @param loginDTO
	 * @return
	 * @throws Exception
	 * @author -- lijiangtao 2020年5月3日 下午4:10:00
	 */
	@Override
	public Object login(LoginDTO loginDTO) throws Exception {
		// 校验验证码
		this.checkSecCode();
		// 密码加密
		if (Objects.equals(loginDTO.getIsEncryption(), YesOrNoEnum.NO.getCode())) {
			loginDTO.setPassword(DigestUtils.md5DigestAsHex(loginDTO.getPassword().getBytes()));
		}
		// 根据用户名和密码去读取用户信息
		UserDTO userDTO = this.getUserInfo(loginDTO);
		if (Objects.isNull(userDTO)) {
			throw new ServiceException(Const.USER_NOT_FOUND);
		}
		TokenParameter tokenParameter = new TokenParameter();
		// 设置用户标识
		tokenParameter.setLoginCode(loginDTO.getUserCode());
		// 设置登录时间
		tokenParameter.setLoginTs(System.currentTimeMillis());
		TokenInfo tokenInfo = TokenGenerator.genToken(tokenParameter);
		shiroTokenCacheManager.registOnlineSession(tokenParameter.getLoginCode(), tokenInfo.getToken());
		tokenInfo.setUserId(userDTO.getId()).setUserCode(userDTO.getUserCode()).setUserName(userDTO.getUserName());

		//添加登录成功日志
		IamLog iamLog = new IamLog();
		iamLog.setId(GeneratIDUtil.getUUId());
		iamLog.setLogTime(new Date());
		iamLog.setLogUser(userDTO.getId());
		iamLog.setLogType(LogTypeEnum.LOGIN_TYPE.getCode());
		iamLog.setLogNote("welcome "+userDTO.getUserName()+"  login , success !");
		logDao.insert(iamLog);
		return tokenInfo;
	}

	/**
	 * 获取 HttpServletRequest
	 *
	 * @return {HttpServletRequest}
	 */
	private HttpServletRequest getRequest() {
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		return (requestAttributes == null) ? null : ((ServletRequestAttributes) requestAttributes).getRequest();
	}

	/**
	 * 判断登录
	 *
	 * @param loginDTO
	 * @return
	 * @author -- lijiangtao 2020年5月4日 下午2:01:16
	 */
	private UserDTO getUserInfo(LoginDTO loginDTO) {
		IamUserSqlBuilder sqlBuilder = new IamUserSqlBuilder();
		sqlBuilder.andUserCodeEqualTo(loginDTO.getUserCode()).andPasswordEqualTo(loginDTO.getPassword());
		sqlBuilder.andTenantCodeEqualTo(loginDTO.getTenantCode()).andIsDeletedEqualTo(YesOrNoEnum.NO.getCode());
		return UserConvert.INSTANCE.entityToDto(userDao.selectBySqlBuilder(sqlBuilder));
	}

	/**
	 * 校验验证码
	 * 
	 * @author -- lijiangtao 2020年7月17日 下午3:37:20
	 */
	private void checkSecCode() {
		HttpServletRequest request = this.getRequest();
		String key = request.getHeader(Const.CAPTCHA_HEADER_KEY);
		String code = request.getHeader(Const.CAPTCHA_HEADER_CODE);
		// 获取验证码
		String redisCode = String.valueOf(redisTemplate.opsForValue().get(Const.CAPTCHA_KEY + key));
		// 判断验证码
		if (code == null || !Objects.deepEquals(redisCode, code)) {
			throw new ServiceException(Const.CAPTCHA_NOT_CORRECT);
		}
	}

	/**
	 * 判断 用户是否需要修改密码。。
	 * @param userid
	 * @return
	 */
	@Override
	public Map judgeLogin(String userid){

		HashMap result = new HashMap<String,Object>();

		//UserDTO userDTO = getUser(tenantCode,userCode);

		boolean firstLogin = this.isFirstLogin(userid);
		boolean flag = this.passwordOverdue(userid);

		if(firstLogin||flag){
			result.put("need",YesOrNoEnum.YES.getCode());
			if(firstLogin){
				result.put("msg","首次登录，需修改密码！");
			}else if(flag){
				result.put("msg","密码已过期，请修改密码！");
			}

		}else{
			result.put("need",YesOrNoEnum.NO.getCode());
			result.put("msg","");
		}

		return result;
	}
	/**获取用户信息*/
	private UserDTO getUser(String tenantCode, String userCode) {
		IamUserSqlBuilder sqlBuilder = new IamUserSqlBuilder();
		sqlBuilder.andTenantCodeEqualTo(tenantCode).andUserCodeEqualTo(userCode).andIsDeletedEqualTo(YesOrNoEnum.NO.getCode());
		return UserConvert.INSTANCE.entityToDto(userDao.selectBySqlBuilder(sqlBuilder));
	}
    @Override
    public Map<String,String> loginOut(String usercode) {
		Map<String,String> map = new HashMap<>();
        redisTemplate.delete(usercode);
        map.put("msg","退出成功！");
       return map;
    }
	/**
	 * 判断是否首次登录
	 * @param userid
	 * @return
	 */
	private boolean isFirstLogin(String userid){

		IamLogSqlBuilder iamLogSqlBuilder = new IamLogSqlBuilder();
		iamLogSqlBuilder.andLogUserEqualTo(userid)
				.andLogTypeEqualTo(LogTypeEnum.LOGIN_TYPE.getCode());
		List<IamLog> iamLogs = logDao.selectListBySqlBuilder(iamLogSqlBuilder);

		if(!iamLogs.isEmpty() && iamLogs.size()>1){
			return false;
		}else{
			return true;
		}
	}

	/**
	 * 判断用户密码是否已超过有效期 90天
	 * @param userid
	 * @return
	 */
	private boolean passwordOverdue(String userid){

		IamLogSqlBuilder iamLogSqlBuilder = new IamLogSqlBuilder();
		iamLogSqlBuilder.andLogUserEqualTo(userid)
				.andLogTypeEqualTo(LogTypeEnum.UPDATE_PWD_TYPE.getCode())
				.orderByLogTime(true);
		iamLogSqlBuilder.setIndexNo(0);
		iamLogSqlBuilder.setPageSize(2);

		List<IamLog> iamLogs = logDao.selectListBySqlBuilder(iamLogSqlBuilder);

		boolean flag = false;

		if(!iamLogs.isEmpty() && iamLogs.size()>0){
			IamLog iamLog = iamLogs.get(0);
			Date logTime = iamLog.getLogTime();

			Instant now = Instant.now();
			Instant lastTime = Instant.ofEpochMilli(logTime.getTime());

			//上次密码修改时间距今大于90天
			if(lastTime.plus(Duration.ofDays(90)).isBefore(now)){
				flag = true;
			}
		}else{
			flag = true;
		}

		return flag;
	}
}
